

#include "luat_base.h"
#include "luat_malloc.h"
#include "luat_fs.h"
#include "stdio.h"
#include "luat_msgbus.h"
#include "luat_timer.h"

#include "luat_ota.h"

#define LUAT_LOG_TAG "main"
#include "luat_log.h"

static int report (lua_State *L, int status);

lua_State *L;

static uint8_t boot_mode = 1;

void stopboot(void) {
  boot_mode = 0;
}

// lua_State * luat_get_state() {
//   return L;
// }

int luat_search_module(const char* name, char* filename);
void luat_os_print_heapinfo(const char* tag);
void luat_force_gc_all(void)
{
	lua_gc(L, LUA_GCCOLLECT, 0);
}
int luat_main_demo() { // 这是验证LuatVM最基础的消息/定时器/Task机制是否正常
  return luaL_dostring(L, "local sys = require \"sys\"\n"
                          "log.info(\"main\", os.date())\n"
                          "leda = gpio.setup(3, 0)"
                          "sys.taskInit(function ()\n"
                          "  while true do\n"
                          "    log.info(\"hi\", rtos.meminfo())\n"
                          "    sys.wait(500)\n"
                          "    leda(1)\n"
                          "    sys.wait(500)\n"
                          "    leda(0)\n"
                          "    log.info(\"main\", os.date())\n"
                          "  end\n"
                          "end)\n"
                          "sys.run()\n");
}

static int pmain(lua_State *L) {
    int re = -2;
    char filename[32] = {0};

    //luat_os_print_heapinfo("boot");

    // 加载内置库
    luat_openlibs(L);

    luat_os_print_heapinfo("loadlibs");

    lua_gc(L, LUA_GCSETPAUSE, 90); // 设置`垃圾收集器间歇率`要低于100%

#ifdef LUAT_HAS_CUSTOM_LIB_INIT
    luat_custom_init(L);
#endif

    if (re == -2) {
      #ifndef LUAT_MAIN_DEMO
      if (luat_search_module("main", filename) == 0) {
        re = luaL_dofile(L, filename);
      }
      else {
        re = -1;
        luaL_error(L, "module '%s' not found", "main");
      }
      #else
      re = luat_main_demo();
      #endif
    }
        
    report(L, re);
    lua_pushboolean(L, re == LUA_OK);  /* signal no errors */
    return 1;
}

/*
** Prints an error message, adding the program name in front of it
** (if present)
*/
static void l_message (const char *pname, const char *msg) {
  if (pname) LLOGE("%s: ", pname);
  LLOGE("%s\n", msg);
}


/*
** Check whether 'status' is not OK and, if so, prints the error
** message on the top of the stack. It assumes that the error object
** is a string, as it was either generated by Lua or by 'msghandler'.
*/
static int report (lua_State *L, int status) {
  if (status != LUA_OK) {
    const char *msg = lua_tostring(L, -1);
    l_message("LUAT", msg);
    lua_pop(L, 1);  /* remove message */
  }
  return status;
}

static int panic (lua_State *L) {
  LLOGE("PANIC: unprotected error in call to Lua API (%s)\n",
                        lua_tostring(L, -1));
  return 0;  /* return to Lua to abort */
}


int luat_main_call(void) {
  // 4. init Lua State
  int status = 0;
  int result = 0;
  L = lua_newstate(luat_heap_alloc, NULL);
  if (L == NULL) {
    l_message("lua", "cannot create state: not enough memory\n");
    goto _exit;
  }
  if (L) lua_atpanic(L, &panic);
  //print_list_mem("after lua_newstate");
  lua_pushcfunction(L, &pmain);  /* to call 'pmain' in protected mode */
  //lua_pushinteger(L, argc);  /* 1st argument */
  //lua_pushlightuserdata(L, argv); /* 2nd argument */
  status = lua_pcall(L, 0, 1, 0);  /* do the call */
  result = lua_toboolean(L, -1);  /* get result */
  report(L, status);
  //lua_close(L);
_exit:
  return result;
}

/**
 * 常规流程, 单一入口, 执行脚本.
 * require "sys"
 * 
 * ... 用户代码 ....
 * 
 * sys.run() 
*/
int luat_main (void) {
  if (boot_mode == 0) {
    return 0; // just nop
  }
  #ifdef LUAT_BSP_VERSION
  LLOGI("LuatOS@%s core %s bsp %s", luat_os_bsp(), LUAT_VERSION, LUAT_BSP_VERSION);
  LLOGI("ROM Build: " __DATE__ " " __TIME__);
  // #if LUAT_VERSION_BETA
  // LLOGD("This is a beta/snapshot version, for testing");
  // #endif
  #else
  LLOGI("LuatOS@%s %s, Build: " __DATE__ " " __TIME__, luat_os_bsp(), LUAT_VERSION);
  #if LUAT_VERSION_BETA
  LLOGD("This is a beta version, for testing");
  #endif
  #endif

  // 1. 初始化文件系统
  luat_fs_init();
  //只有302启用老版OTA
#ifdef AIR302
    // 是否需要升级或者回滚
    luat_ota_update_or_rollback();
#endif

  int result = luat_main_call();
  LLOGE("Lua VM exit!! reboot in %dms", LUAT_EXIT_REBOOT_DELAY);
  luat_ota_reboot(LUAT_EXIT_REBOOT_DELAY);
  // 往下是肯定不会被执行的
  return 0;
}



#include "rotable.h"
void luat_newlib(lua_State* l, const rotable_Reg* reg) {
  #ifdef LUAT_CONF_DISABLE_ROTABLE
  luaL_newlibtable(l,reg);
  int i;
  int nup = 0;

  luaL_checkstack(l, nup, "too many upvalues");
  for (; reg->name != NULL; reg++) {  /* fill the table with given functions */
        for (i = 0; i < nup; i++)  /* copy upvalues to the top */
            lua_pushvalue(l, -nup);
        if (reg->func)
            lua_pushcclosure(l, reg->func, nup);  /* closure with those upvalues */
        else
            lua_pushinteger(l, reg->value);
        lua_setfield(l, -(nup + 2), reg->name);
    }
    lua_pop(l, nup);  /* remove upvalues */
  #else
  rotable_newlib(l, reg);
  #endif
}

void luat_os_print_heapinfo(const char* tag) {
    size_t total; size_t used; size_t max_used;
    luat_meminfo_luavm(&total, &used, &max_used);
    LLOGD("%s luavm %ld %ld %ld", tag, total, used, max_used);
    luat_meminfo_sys(&total, &used, &max_used);
    LLOGD("%s sys   %ld %ld %ld", tag, total, used, max_used);
}


//唯一c等待接口使用的id
uint64_t c_wait_id = 0;
//c等待接口
//获取一个消息等待的唯一id
//并向栈中推入一个可等待对象
//返回值为0时，表示用户没有启用sys库，无法使用该功能
//[-0, +1, –]
uint64_t luat_pushcwait(lua_State *L) {
    if(lua_getglobal(L, "sys_cw") != LUA_TFUNCTION)
    {
        LLOGE("sys lib not found!");
        return 0;
    }
    c_wait_id++;
    char* topic = (char*)luat_heap_malloc(1 + sizeof(uint64_t));
    topic[0] = 0x01;//前缀用一个不可见字符，防止和用户用的重复
    memcpy(topic + 1,&c_wait_id,sizeof(uint64_t));
    lua_pushlstring(L,topic,1 + sizeof(uint64_t));
    luat_heap_free(topic);
    lua_call(L,1,1);
    return c_wait_id;
}

/*
@sys_pub sys
用于luatos内部的系统消息传递
以0x01为第一个字节开头
@args 返回的数据
@usage
--此为系统内部使用的消息，请勿在外部使用
*/
static int luat_cbcwait_cb(lua_State *L, void* ptr) {
    rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
    if(lua_getglobal(L, "sys_pub") != LUA_TFUNCTION)
        return 0;
    char* topic = (char*)luat_heap_malloc(1 + sizeof(uint64_t));
    topic[0] = 0x01;
    memcpy(topic + 1,msg->ptr,sizeof(uint64_t));
    lua_pushlstring(L,topic,1 + sizeof(uint64_t));
    luat_heap_free(topic);
    luat_heap_free(msg->ptr);
    lua_call(L, 1, 0);
    return 0;
}

//c等待接口的回调，最简单的无参数的回调
//要返回参数的话，参考上面自己写一个
void luat_cbcwait_noarg(uint64_t id) {
    if(id == 0)
        return;
    rtos_msg_t msg = {0};
    msg.handler = luat_cbcwait_cb;
    uint64_t* idp = (uint64_t*)luat_heap_malloc(sizeof(uint64_t));
    memcpy(idp, &id, sizeof(uint64_t));
    msg.ptr = (void*)idp;
    luat_msgbus_put(&msg, 0);
}
